/* 
 *  版本：miniOS 0.3
 *  功能：时间盘轮转，延时调度，无优先级
 *  作者：https://gitee.com/Amate123/miniOS0.3
 *  时间：2024-11-03
 *  修改：适配Cotex-M0内核，R4-R11现场保存和恢复分开进行
 */

#include "minios_cfg.h"


/* 最高级任务 */
struct 		OS_TCB 					*OSTask_HighestTcb;

/* 空闲任务TCB */
#define		OS_STACK_SIZE			30
struct 		OS_TCB 					MINI_IDEL_TASK_TCB = {0};
static 		OS_UINT32 				OS_IDLE_STACK[OS_STACK_SIZE] = {0};

/* 就绪链表 */
struct 		OS_RDY_LIST 			*rdy_list;
//OS_INT16								OS_RDY_HASH_LIST[TASK_MAX_CNT] = {0};					// 任务就绪哈希列表
//struct 		OS_TCB					*OS_RDY_HASH_TCB_LIST[TASK_MAX_CNT] = {0};		// 任务就绪哈希映射TCB列表

/* 睡眠链表 */
struct 		OS_SLEEP_LIST 			*sleep_list;

/* OS变量 */
struct 		OS_TCB 					*OSTask_CurTcb;    				// 当前任务的TCB
// OS_UINT8  							OS_Running = 0;						// OS运行状态



/*
 * 函数: MINI_OSInit
 * 描述: 初始化miniOS，
 * 		 初始化 rdy_list & sleep_list , 创建空闲任务
 * 参数: 无
 * 返回值: 无
 */
void MINI_OSInit(void)
{
	OS_INT32 INTS = 0;
	INTS = CPU_INT_Save();
	
	// 初始化 就绪链表
	rdy_list = (struct OS_RDY_LIST *)malloc(sizeof(struct OS_RDY_LIST));
	rdy_list->head = NULL;
	rdy_list->tail = NULL;
	rdy_list->rdycnt = 0;
	
	// 初始化 睡眠链表
	sleep_list = (struct OS_SLEEP_LIST *)malloc(sizeof(struct OS_SLEEP_LIST));
	sleep_list->head = NULL;
	sleep_list->tail = NULL;
	sleep_list->slpcnt = 0;
	
	// 创建 空闲任务
	MINI_CreatTask(&MINI_IDEL_TASK_TCB, OS_IDLE_TASK, NULL, &OS_IDLE_STACK[0], OS_STACK_SIZE);
	
	CPU_INT_Restore(INTS);
}


/*
 * 函数: MINI_RdyLs_Del_Task
 * 描述: 根据传入的 tcb 删除就绪链表中的任务
 * 参数: 要删除的任务tcb
 * 返回值: 无
 * 注意: 删除的时候要注意修改头尾指针
 */
void MINI_RdyLs_Del_Task(struct OS_TCB *tcb)
{
	// 如果是头结点
	if(tcb->front == NULL)
	{
		// 如果也是尾结点，说明只有一个节点
		if(tcb->next == NULL)
		{
			rdy_list->head = NULL;
			rdy_list->tail = NULL;
			rdy_list->rdycnt = 0;
		}
		// 否则更新头结点
		else
		{
			rdy_list->head = tcb->next;
			tcb->next->front = NULL;
			rdy_list->rdycnt--;
		}
	}
	// 如果是尾节点
	else if(tcb->next == NULL)
	{
		rdy_list->tail = tcb->front;  // 更新尾结点
		tcb->front->next = NULL;
		rdy_list->rdycnt--;
	}
	else
	{
		tcb->front->next = tcb->next;
		if(tcb->next != NULL)
			tcb->next->front = tcb->front;
		rdy_list->rdycnt--;
	}
	
	tcb->front = NULL;
	tcb->next = NULL;
}


/*
 * 函数: MINI_RdyLs_Add_Task
 * 描述: 添加任务到就绪链表
 * 参数: 要添加的任务 tcb
 * 返回值: 无
 */
void MINI_RdyLs_Add_Task(struct OS_TCB *tcb)
{
	if(rdy_list->rdycnt == 0)   // 就绪链表为空
	{
		// 修改就绪链表头尾指针
		rdy_list->head = tcb;
		rdy_list->tail = tcb;
		rdy_list->rdycnt = 1;
		
		// 修改任务TCB前后指针
		tcb->front = NULL;
		tcb->next = NULL;

	}
	else
	{
		rdy_list->tail->next = tcb;
		tcb->front = rdy_list->tail;
		tcb->next = NULL;
		rdy_list->tail = tcb;
		rdy_list->rdycnt++;
	}
}


/*
 * 函数: MINI_SlpLs_Del_Task
 * 描述: 删除睡眠链表中的任务
 * 参数: 要添加的任务 tcb
 * 返回值: 无
 * 注意: 删除的时候要注意修改头尾指针
 */
void MINI_SlpLs_Del_Task(struct OS_TCB *tcb)
{
	// 如果是头结点
	if(tcb->front == NULL)
	{
		// 如果只有一个节点
		if(tcb->next == NULL)
		{
			sleep_list->head = NULL;
			sleep_list->tail = NULL;
			sleep_list->slpcnt = 0;
		}
		else
		{
			sleep_list->head = tcb->next;
			tcb->next->front = NULL;
			sleep_list->slpcnt--;
		}
	}
	
	// 如果是尾节点
	else if(tcb->next == NULL)
	{
		sleep_list->tail = tcb->front;  // 更新尾结点
		tcb->front->next = NULL;
		sleep_list->slpcnt--;
	}
	
	else
	{
		tcb->front->next = tcb->next;
		if(tcb->next != NULL)
			tcb->next->front = tcb->front;
		sleep_list->slpcnt--;
	}
	
	// 如果删除完后只剩一个节点，则头指针和尾指针相同
	if(sleep_list->slpcnt == 1)  
	{
		sleep_list->tail = sleep_list->head;
	}
	
	tcb->front = NULL;
	tcb->next = NULL;
}


/*
 * 函数: MINI_SleepLs_Add_Task
 * 描述: 添加任务到睡眠链表
 * 参数: 要添加的任务 tcb
 * 返回值: 无
 */
void MINI_SleepLs_Add_Task(struct OS_TCB *tcb)
{
	if(sleep_list->slpcnt == 0)   // 睡眠链表为空
	{
		sleep_list->head = tcb;
		sleep_list->tail = tcb;
		sleep_list->slpcnt = 1;
		tcb->front = NULL;
		tcb->next = NULL;
	}
	else{
		sleep_list->tail->next = tcb;
		tcb->front = sleep_list->tail;
		tcb->next = NULL;
		sleep_list->tail = tcb;
		sleep_list->slpcnt++;
	}
}

/*
 * 函数: MINI_CreatTask
 * 描述: 创建一个任务
 * 		 1. 初始化任务TCB栈起始地址和大小
 * 		 2. 设置 TCB->SP, 设置xPSR, PC, LR, R12, R3, R2, R1，将这些寄存器入栈
 * 		 3. 初始化现场寄存器 R4-R11
 * 参数: tcb - 任务tcb指针
 * 		 task - 任务函数入口地址（任务函数名）
 * 		 arg - 任务入参
 *       stack_base - 任务栈基地址
 *       stack_size - 任 务栈大小（单位字节）
 * 返回值: 无
 */
void MINI_CreatTask(struct OS_TCB *tcb, fun_task task, void *arg, \
                    OS_UINT32 *stack_base, OS_UINT32 stack_size)
{
		int i=0;
	// 任务栈起始地址和大小
	tcb->STACK_BASE = (OS_UINT32*)((OS_UINT32)stack_base + (stack_size-1) * 4);
	tcb->STACK_SIZE = stack_size * 4;

 	 // 初始化任务栈内容
  	tcb->SP = tcb->STACK_BASE;								// SP指向栈顶（一开始栈顶就是栈基址）
  	*tcb->SP = (1 << 24);      								// xPSR, 24位为1：Thumb指令集
  	*(--tcb->SP) = (OS_UINT32)task;     					// PC，返回地址
	*(--tcb->SP) = (OS_UINT32)task;							// LR
	*(--tcb->SP) = 0x12121212u;								// R12
	*(--tcb->SP) = 0x33333333u;								// R3
	*(--tcb->SP) = 0x22222222u;								// R2
	*(--tcb->SP) = 0x11111111u;								// R1
	*(--tcb->SP) = (OS_UINT32)arg;							// R0
	
	for(i = 8; i > 0; i--)
			*(--tcb->SP) = 0x33333333u + (0x11111111u * i);		// R11 - R4
	
	// 如果不是空闲任务，则创建现场，并添到就绪链表
	if(tcb != &MINI_IDEL_TASK_TCB)
	{
		MINI_RdyLs_Add_Task(tcb);
	}
}


/*
 * 函数: SetPendSVPriLowest
 * 描述: 设置PendSV异常优先级最低，
 *       为了适配M0/M3/M4内核，统一设置0xE000ED20开始的第二个字节为0xF0，
 *       MO内核会自动忽略没有使用的第21,20位（参考M0权威指南P134/P158，M3/M4权威指南P161）
 * 参数: 无
 * 返回值: 无
 * 说明: 该函数弃用，使用Set_PendSV_Lowest设置PendSV优先级为最低
 */
void SetPendSVPriLowest(void)
{
	OS_UINT32 temp;
	temp = *((OS_UINT32*)0xE000ED20);
	/*
	 * 对于M0内核    -- 将第23，22位设置为1，最低异常优先级为0xC0
	 * 对于M3/M4内核 -- 将第23，22，21，20位设置为1，最低异常优先级为0xF0
	 */
	temp = (temp & 0xFF00FFFF) | (0x0F << 20);			
	*((OS_UINT32*)0xE000ED20) = temp;							  // 写回到可编程系统异常优先级寄存器
}



/*
 * 函数: MINI_OSStart
 * 描述: 启动 minisOS
 *       1. 设置当前任务 TCB 为空闲任务,
 * 		 	 2. 设置PendSV为最低优先级
 *       3. 触发任务调度
 * 参数: 无
 * 返回值: 无
 */
void MINI_OSStart(void)
{
	// 刚开始的任务设置为IDLE
	OSTask_CurTcb = rdy_list->head;
	OSTask_HighestTcb = OSTask_CurTcb;
	
	Set_PendSV_Lowest();					// 设置PendSV为最低优先级
	
	OS_Run_Highest();							// 运行优先级最高的任务（就绪链表第一个任务）
}


/*
 * 函数: OSTask_SwTcb
 * 描述: 切换任务TCB，如果 rdylist 为不空，设置当前 TCB 为 
 *       rdylist 中的第一个任务，否则设 IDLE 任务为当前任务
 * 参数: 无
 * 返回值: 无
 */
void OSTask_SwTcb(void)
{
	
	if(rdy_list->rdycnt > 0)
		OSTask_HighestTcb = rdy_list->head;				// 切换当前任务为就绪链表中的第一个任务
	else
		OSTask_HighestTcb = &MINI_IDEL_TASK_TCB;		// 如果当前任务就绪链表为空，切换当前任务为空闲任务
	
	OSTask_CurTcb = OSTask_HighestTcb;

}


/*
 * 函数: OSTask_Slp_Chk
 * 描述: 遍历睡眠链表，增加任务睡眠计时、更新睡眠列表和就绪链表，
 *       由 Systick_Handler 调用
 * 参数: 无
 * 返回值: 无
 */
void OSTask_Slp_Chk(void)
{
	struct OS_TCB *slpstk;
	struct OS_TCB *slpstk2;
	
	OS_UINT8	rdy_flag = 0;
	if(sleep_list->slpcnt > 0)									// 如果有任务在睡眠，则更新 sleep_cnt
	{
		slpstk =sleep_list->head;
		do
		{
			slpstk2 = slpstk->next;								// 保存任务TCB的next
			slpstk->Sleep_Cnt++;								// 睡眠时间加1ms
			if(slpstk->Sleep_Cnt == slpstk->Sleep_TimeOut)		// 如果达到了睡眠超时时间
			{
				MINI_SlpLs_Del_Task(slpstk);					// 将任务从睡眠链表中删除
				
				MINI_RdyLs_Add_Task(slpstk);					// 添加任务到就绪链表
				
				slpstk->Sleep_Cnt = 0;
				slpstk->Sleep_TimeOut = 0;
				
				rdy_flag++;										// 标记有任务进入就绪态了
			}
			slpstk = slpstk2;
		}while(slpstk != NULL);
	}
	
	if(rdy_flag > 0)
	{
		OS_Trig_PendSV();       // 触发pendsv中断
	}
}


/*
 * 函数: OS_IDLE_TASK
 * 描述: 空闲任务（不存在就绪链表中）
 * 参数: 传入参数，可在 MINI_OSInit 函数中设置
 * 返回值: 无
 */
void OS_IDLE_TASK(void *arg)
{
	while(1);
}


/*
 * 函数: MINI_OS_Delay
 * 描述: 任务睡眠，设置任务 TCB 中的睡眠相关成员，
 *       将任务从就绪链表中移到睡眠链表中，并触发 PendSV 进行任务调度
 * 参数: 睡眠时长（单位由 OSTickHz 决定，如果该宏为1000，则单位为1/1000s，即1ms）
 * 返回值: 无
 */
void MINI_OS_Delay(OS_UINT32 ms)
{
	OS_INT32 r0 = 0;
	r0 = CPU_INT_Save();
	
	if(OSTask_CurTcb->Sleep_TimeOut > 0)
	{
		CPU_INT_Restore(r0);
		return;
	}
	
	OSTask_CurTcb->Sleep_Cnt = 0;
	OSTask_CurTcb->Sleep_TimeOut = ms;
	
	MINI_RdyLs_Del_Task(OSTask_CurTcb);			// 将当前任务从就绪列表中删除
	
	MINI_SleepLs_Add_Task(OSTask_CurTcb);		// 添加当前任务到睡眠列表中
	
	OS_Trig_PendSV();       // 触发pendsv中断
	
	CPU_INT_Restore(r0);
}


void SysTick_Handler(void)
{
	OS_INT32 r0 = 0;
	r0 = CPU_INT_Save();
	
	OSTask_Slp_Chk();		  // 更新睡眠链表任务
	
	CPU_INT_Restore(r0);
}


